/*
 * This file is part of connotea-java.
 *
 * connotea-java is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * connotea-java is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package org.connotea;

import static java.util.Collections.unmodifiableList;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang.builder.ToStringBuilder.reflectionToString;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.restlet.data.Reference;

/**
 * Citation entity bean.
 * 
 * @author <a href="mailto:christopher.townson@googlemail.com">Christopher
 *         Townson</a>
 */
public final class Citation implements Comparable<Citation>, Serializable {

    private static final long serialVersionUID = 1L;

    private List<Author> authors = new ArrayList<Author>();

    private Long citationId;

    private Date date;

    private String doi;

    private Reference doiResolver;

    private String endingPage;

    private String issn;

    private Long journalId;

    private String number;

    private String publicationName;

    private String pmid;

    private Reference pmidResolver;

    private String startingPage;

    private String title;

    private String volume;

    /**
     * Default, no-argument constructor.
     */
    public Citation() {
        // do nothing;
    }

    /**
     * Copy constructor.
     * 
     * @param citation the citation to copy
     */
    public Citation(Citation citation) {
        setAuthors(citation.getAuthors());
        setCitationId(citation.getCitationId());
        setDate(citation.getDate());
        setDoi(citation.getDoi());
        setDoiResolver(new Reference(citation.getDoiResolver()));
        setEndingPage(citation.getEndingPage());
        setIssn(citation.getIssn());
        setJournalId(citation.getJournalId());
        setNumber(citation.getNumber());
        setPublicationName(citation.getPublicationName());
        setPmid(citation.getPmid());
        setPmidResolver(new Reference(citation.getPmidResolver()));
        setStartingPage(citation.getStartingPage());
        setTitle(citation.getTitle());
        setVolume(citation.getVolume());
    }

    /**
     * Comparability is based on the following fields in order: citationId, doi,
     * pmid, title, authors, date, issn, journalId, volume, number,
     * startingPage, endingPage, publicationName, doiResolver, pmidResolver.
     * 
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    public int compareTo(Citation citation) {
        return new CompareToBuilder().append(citationId, citation.citationId)
                .append(doi, citation.doi).append(pmid, citation.pmid).append(
                        title, citation.title)
                .append(authors, citation.authors).append(date, citation.date)
                .append(issn, citation.issn).append(journalId,
                        citation.journalId).append(volume, citation.volume)
                .append(number, citation.number).append(startingPage,
                        citation.startingPage).append(endingPage,
                        citation.endingPage).append(publicationName,
                        citation.publicationName).append(doiResolver,
                        citation.doiResolver).append(pmidResolver,
                        citation.pmidResolver).toComparison();
    }

    /**
     * Equality is based on the following fields in order: citationId, doi,
     * pmid, title, authors, date, issn, journalId, volume, number,
     * startingPage, endingPage, publicationName, doiResolver, pmidResolver.
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Citation)) {
            return false;
        }
        Citation citation = (Citation) obj;
        return new EqualsBuilder().append(citationId, citation.citationId)
                .append(doi, citation.doi).append(pmid, citation.pmid).append(
                        title, citation.title)
                .append(authors, citation.authors).append(date, citation.date)
                .append(issn, citation.issn).append(journalId,
                        citation.journalId).append(volume, citation.volume)
                .append(number, citation.number).append(startingPage,
                        citation.startingPage).append(endingPage,
                        citation.endingPage).append(publicationName,
                        citation.publicationName).append(doiResolver,
                        citation.doiResolver).append(pmidResolver,
                        citation.pmidResolver).isEquals();
    }

    /**
     * Returns the authors.
     * 
     * @return the authors
     */
    public List<Author> getAuthors() {
        return unmodifiableList(authors);
    }

    /**
     * Returns the citationId.
     * 
     * @return the citationId
     */
    public Long getCitationId() {
        return citationId;
    }

    /**
     * Returns the date.
     * 
     * @return the date
     */
    public Date getDate() {
        return new Date(date.getTime());
    }

    /**
     * Returns the doi.
     * 
     * @return the doi
     */
    public String getDoi() {
        return doi;
    }

    /**
     * Returns the doiResolver.
     * 
     * @return the doiResolver
     */
    public Reference getDoiResolver() {
        return doiResolver;
    }

    /**
     * Returns the endingPage.
     * 
     * @return the endingPage
     */
    public String getEndingPage() {
        return endingPage;
    }

    /**
     * Returns the issn.
     * 
     * @return the issn
     */
    public String getIssn() {
        return issn;
    }

    /**
     * Returns the journalId.
     * 
     * @return the journalId
     */
    public Long getJournalId() {
        return journalId;
    }

    /**
     * Returns the number.
     * 
     * @return the number
     */
    public String getNumber() {
        return number;
    }

    /**
     * Returns the pmid (PubMed ID).
     * 
     * @return the pmid
     */
    public String getPmid() {
        return pmid;
    }

    /**
     * Returns the pmidResolver (URI reference which can be used to resolve the
     * pmid).
     * 
     * @return the pmidResolver
     */
    public Reference getPmidResolver() {
        return pmidResolver;
    }

    /**
     * Returns the publicationName.
     * 
     * @return the publicationName
     */
    public String getPublicationName() {
        return publicationName;
    }

    /**
     * Returns the startingPage.
     * 
     * @return the startingPage
     */
    public String getStartingPage() {
        return startingPage;
    }

    /**
     * Returns the title.
     * 
     * @return the title
     */
    public String getTitle() {
        return title;
    }

    /**
     * Returns the volume.
     * 
     * @return the volume
     */
    public String getVolume() {
        return volume;
    }

    /**
     * HashCode is based on the following fields in order: citationId, doi,
     * pmid, title, authors, date, issn, journalId, volume, number,
     * startingPage, endingPage, publicationName, doiResolver, pmidResolver.
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(citationId).append(doi)
                .append(pmid).append(title).append(authors).append(date)
                .append(issn).append(journalId).append(volume).append(number)
                .append(startingPage).append(endingPage)
                .append(publicationName).append(doiResolver).append(
                        pmidResolver).toHashCode();
    }

    /**
     * Sets the authors.
     * 
     * @param authors the authors to set
     */
    public void setAuthors(List<Author> authors) {
        if (isNotEmpty(authors)) {
            this.authors.addAll(authors);
        }
    }

    /**
     * Sets the citationId.
     * 
     * @param citationId the citationId to set
     */
    public void setCitationId(Long citationId) {
        this.citationId = citationId;
    }

    /**
     * Sets the date.
     * 
     * @param date the date to set
     */
    public void setDate(Date date) {
        this.date = new Date(date.getTime());
    }

    /**
     * Sets the doi.
     * 
     * @param doi the doi to set
     */
    public void setDoi(String doi) {
        this.doi = doi;
    }

    /**
     * Sets the doiResolver.
     * 
     * @param doiResolver the doiResolver to set
     */
    public void setDoiResolver(Reference doiResolver) {
        this.doiResolver = doiResolver;
    }

    /**
     * Sets the endingPage.
     * 
     * @param endingPage the endingPage to set
     */
    public void setEndingPage(String endingPage) {
        this.endingPage = endingPage;
    }

    /**
     * Sets the issn.
     * 
     * @param issn the issn to set
     */
    public void setIssn(String issn) {
        this.issn = issn;
    }

    /**
     * Sets the journalId.
     * 
     * @param journalId the journalId to set
     */
    public void setJournalId(Long journalId) {
        this.journalId = journalId;
    }

    /**
     * Sets the number.
     * 
     * @param number the number to set
     */
    public void setNumber(String number) {
        this.number = number;
    }

    /**
     * Sets the pmid.
     * 
     * @param pmid the pmid to set
     */
    public void setPmid(String pmid) {
        this.pmid = pmid;
    }

    /**
     * Sets the pmidResolver.
     * 
     * @param pmidResolver the pmidResolver to set
     */
    public void setPmidResolver(Reference pmidResolver) {
        this.pmidResolver = pmidResolver;
    }

    /**
     * Sets the publicationName.
     * 
     * @param publicationName the publicationName to set
     */
    public void setPublicationName(String publicationName) {
        this.publicationName = publicationName;
    }

    /**
     * Sets the startingPage.
     * 
     * @param startingPage the startingPage to set
     */
    public void setStartingPage(String startingPage) {
        this.startingPage = startingPage;
    }

    /**
     * Sets the title.
     * 
     * @param title the title to set
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * Sets the volume.
     * 
     * @param volume the volume to set
     */
    public void setVolume(String volume) {
        this.volume = volume;
    }

    /**
     * Pretty-prints the current object state.
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return reflectionToString(this);
    }
}
